package com.example.gmall.search.service;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.pms.entity.BrandEntity;
import com.atguigu.gmall.pms.entity.CategoryEntity;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.example.gmall.search.pojo.Goods;
import com.example.gmall.search.pojo.SearchParamVo;
import com.example.gmall.search.pojo.SearchResopnseAttrVo;
import com.example.gmall.search.pojo.SearchResponseVo;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class SearchService {
    @Resource
    private RestHighLevelClient restHighLevelClient;
    public SearchResponseVo search(SearchParamVo searchParamVo) {
        try {
            //创建dsl语句,选择要搜索的表
            SearchRequest searchRequest = new SearchRequest(new String[]{"goods"}, builder(searchParamVo));
            //获取dsl语句搜索出来的对象信息
            SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            //解析结果集并返回封装的SearchResponseVo
            SearchResponseVo searchResponseVo = parseResult(search);
            //设置分页
            searchResponseVo.setPageNum(searchParamVo.getPageNum());
            searchResponseVo.setPageSize(searchParamVo.getPageSize());
            //将封装好的SearchResponseVo返回给页面
            return searchResponseVo;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 从SearchResponse解析结果转换成SearchResponseVo
     */
    private SearchResponseVo parseResult(SearchResponse searchResponse){
        //要返回给页面的参数
        SearchResponseVo searchResponseVo = new SearchResponseVo();

        //解析搜索结果集，获取分页信息
        SearchHits hits = searchResponse.getHits();
        long value = hits.getTotalHits().value;
        searchResponseVo.setTotal(value);//设置totle

        SearchHit[] hitsHits = hits.getHits();
        //把hitsHits数据转换成goods集合,设置高亮
        searchResponseVo.setGoodsList(Arrays.stream(hitsHits).map(hitsHit->{

            //获取hitsHit中的json字符串
            String json = hitsHit.getSourceAsString();
            //把json反序列化成goods对象
            Goods goods = JSON.parseObject(json, Goods.class);

            //获取高亮结果集，解析后并设置
            Map<String, HighlightField> highlightFields = hitsHit.getHighlightFields();
            if (CollectionUtils.isNotEmpty(highlightFields)){//非空
                HighlightField title = highlightFields.get("title");//获取高亮字段
                if (title!=null){
                    Text[] fragments = title.getFragments();//获取高亮文本
                    if (fragments!=null && fragments.length>0){
                        Text fragment = fragments[0];
                        if (fragments!=null){
                            String highLightText = fragment.string();//转换成string
                            goods.setTitle(highLightText);//设置高亮字段
                        }
                    }
                }
            }
            return goods;
        }).collect(Collectors.toList()));



        //解析聚合结果集，获取过滤条件列表
        Aggregations aggregations = searchResponse.getAggregations();

        //获取品牌的聚合结果集
        ParsedLongTerms brandIdAgg = aggregations.get("brandIdAgg");
        //获取品牌id桶
        List<? extends Terms.Bucket> brandIdAggBuckets = brandIdAgg.getBuckets();
        if (CollectionUtils.isNotEmpty(brandIdAggBuckets)){
            /**
             * 设置品牌桶过滤条件
             */
            searchResponseVo.setBrands(//设置brands
            brandIdAggBuckets.stream().map(bucket -> {
                //将品牌桶转换成BrandEntity对象
                BrandEntity brandEntity = new BrandEntity();

                brandEntity.setId(bucket.getKeyAsNumber().longValue());
                //获取子聚合
                Aggregations subAggs = bucket.getAggregations();
                //获取品牌名称的子聚合
                ParsedStringTerms brandNameAgg = subAggs.get("brandNameAgg");
                //获取品牌名称桶
                List<? extends Terms.Bucket> brandNameAggBuckets = brandNameAgg.getBuckets();
                if (CollectionUtils.isNotEmpty(brandNameAggBuckets)){
                    //设置品牌名称
                    brandEntity.setName(brandNameAggBuckets.get(0).getKeyAsString());
                }

                //获取品牌logo的子聚合
                ParsedStringTerms logoAgg = subAggs.get("logoAgg");
                //获取品牌logo桶
                List<? extends Terms.Bucket> logoAggBuckets = logoAgg.getBuckets();
                if (CollectionUtils.isNotEmpty(logoAggBuckets)){
                    //设置品牌logo
                    brandEntity.setLogo(logoAggBuckets.get(0).getKeyAsString());
                }

                return brandEntity;
            }).collect(Collectors.toList()));
        }



        //获取分类聚合结果集
        ParsedLongTerms categoryIdAgg = aggregations.get("categoryIdAgg");
        //获取分类桶
        List<? extends Terms.Bucket> categoryIdAggBuckets = categoryIdAgg.getBuckets();
        if (CollectionUtils.isNotEmpty(categoryIdAggBuckets)){
            /**
             * 设置分类过滤条件
             */
            searchResponseVo.setCategories(//设置分类参数
            categoryIdAggBuckets.stream().map(categoryAgg->{
                //创建分类对象
                CategoryEntity categoryEntity = new CategoryEntity();
                //设置id
                categoryEntity.setId(categoryAgg.getKeyAsNumber().longValue());
                //获取分类名称的子聚合
                ParsedStringTerms subAggs = categoryAgg.getAggregations().get("categoryNameAgg");
                //获取分类名称桶
                List<? extends Terms.Bucket> subAggsBuckets = subAggs.getBuckets();
                if (CollectionUtils.isNotEmpty(subAggsBuckets)){
                    categoryEntity.setName(subAggsBuckets.get(0).getKeyAsString());
                }
                return categoryEntity;
            }).collect(Collectors.toList()));
        }


        //获取规格参数的嵌套结果集
        ParsedNested attrAgg = aggregations.get("attrAgg");
        //获取规格参数Id的子聚合
        ParsedLongTerms attrAggAggregations = attrAgg.getAggregations().get("attrIdAgg");
        //获取规格参数桶
        List<? extends Terms.Bucket> aggregationsBuckets = attrAggAggregations.getBuckets();
        if (CollectionUtils.isNotEmpty(aggregationsBuckets)){
            /**
             * 设置规格参数过滤条件
             */
            searchResponseVo.setFilters(aggregationsBuckets.stream().map(attrBuckets->{
                //创建规格参数对象
                SearchResopnseAttrVo searchResopnseAttrVo = new SearchResopnseAttrVo();
                //设置id
                searchResopnseAttrVo.setAttrId(attrBuckets.getKeyAsNumber().longValue());

                //获取规格参数名称聚合
                ParsedStringTerms attrNameAgg = attrBuckets.getAggregations().get("attrNameAgg");
                //获取规格参数桶
                List<? extends Terms.Bucket> attrNameAggBuckets = attrNameAgg.getBuckets();
                if (CollectionUtils.isNotEmpty(attrNameAggBuckets)){
                    //设置规格参数对象的名称
                    searchResopnseAttrVo.setAttrName(attrNameAggBuckets.get(0).getKeyAsString());
                }

                //设置规格参数值聚合
                ParsedStringTerms attrValueAgg = attrBuckets.getAggregations().get("attrValueAgg");
                //设置规格参数值桶
                List<? extends Terms.Bucket> attrValueAggBuckets = attrValueAgg.getBuckets();
                if (CollectionUtils.isNotEmpty(attrValueAggBuckets)){
                    //设置规格参数对象的值
                    searchResopnseAttrVo.setAttrValues(
                            attrValueAggBuckets.stream()
                                    //因为值是一个集合，所以需要将值中的Key取出，并且转换成新的集合
                                    .map(MultiBucketsAggregation.Bucket::getKeyAsString)
                                    .collect(Collectors.toList()));
                }

                return searchResopnseAttrVo;
            }).collect(Collectors.toList()));
        }

        return searchResponseVo;
    }


    /**
     * 构建Es的dsl语句
     */
    private SearchSourceBuilder builder(SearchParamVo paramVo){
        //构建搜索请求的dsl源
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        String keyword = paramVo.getKeyword();//获取搜索关键字
        if (StringUtils.isBlank(keyword)){
            //抛出自定义异常
            throw new RuntimeException("请输入查询条件");
        }


        //构建查询过滤条件
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //构建匹配查询
        boolQuery.must(QueryBuilders.matchQuery("title",keyword).operator(Operator.AND));
        sourceBuilder.query(boolQuery);


        //构建排序条件
            //构建品牌的词条过滤
        List<Long> brandId = paramVo.getBrandId();
        if (CollectionUtils.isNotEmpty(brandId)) {
            boolQuery.filter(QueryBuilders.termsQuery("brandId",brandId));
        }
            //构建分类的词条过滤
        List<Long> categoryId = paramVo.getCategoryId();
        if (CollectionUtils.isNotEmpty(categoryId)) {
            boolQuery.filter(QueryBuilders.termsQuery("categoryId", categoryId));
        }
            //构建价格区间的过滤
        Double priceFrom = paramVo.getPriceFrom();
        Double priceTo = paramVo.getPriceTo();
        if (priceFrom != null || priceTo != null) {
            //判断起止和截止价格是否为空
            RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("price");
            if (priceFrom != null){
                rangeQuery.gte(priceFrom);
            }
            if (priceTo != null){
                rangeQuery.lte(priceTo);
            }
            boolQuery.filter(rangeQuery);
        }
            //仅显示有货的词条过滤
        Boolean store = paramVo.getStore();
        if (store != null) {
            boolQuery.filter(QueryBuilders.termQuery("store",store));
        }
            //构建规格参数的嵌套过滤
        List<String> props = paramVo.getProps();
        if (CollectionUtils.isNotEmpty(props)){
            //遍历props，每一个prop就要对应每一个嵌套过滤
            props.forEach(prop->{
                //解析字符串，获取规格参数id和value
                //先将prop进行分割，按照:进行分割
                String[] attr = StringUtils.split(prop, ":");
                //如果分割完数组长度为2并且不为null，并且第一位是数字就进去执行
                if (attr != null && attr.length == 2 && StringUtils.isNumeric(attr[0])) {
                    BoolQueryBuilder booleanQuery = QueryBuilders.boolQuery();
                    //规格参数id的过滤
                    booleanQuery.must(QueryBuilders.termQuery("searchAttrs.attrId", attr[0]));
                    //规格参数值的过滤
                    booleanQuery.must(QueryBuilders.termsQuery(
                            "searchAttrs.attrValue", StringUtils.split(attr[1], "-")));
                    boolQuery.filter(QueryBuilders.nestedQuery("searchAttrs", booleanQuery, ScoreMode.None));
                }
            });
        }





        //构建排序条件
        //排序条件，0-得分降序，1-价格降序，2-价格升序，3-销量降序，4-新品降序
        Integer sort = paramVo.getSort();
        switch (sort){
            case 1: sourceBuilder.sort("price", SortOrder.DESC);break;
            case 2: sourceBuilder.sort("price", SortOrder.ASC);break;
            case 3: sourceBuilder.sort("sales", SortOrder.DESC);break;
            case 4: sourceBuilder.sort("createTime", SortOrder.DESC);break;
            default:sourceBuilder.sort("_score",SortOrder.DESC);
        }


        //分页
        Integer pageNum = paramVo.getPageNum();
        Integer pageSize = paramVo.getPageSize();
        sourceBuilder.from((pageNum-1) * pageSize).size(pageSize);


        //构建高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title")
                .preTags("<font style='color:red;'>").postTags("</front>");
        sourceBuilder.highlighter(highlightBuilder);


        //构建聚合
        //品牌的聚合
        sourceBuilder.aggregation(AggregationBuilders.terms("brandIdAgg").field("brandId")
                .subAggregation(AggregationBuilders.terms("brandNameAgg").field("brandName"))
                .subAggregation(AggregationBuilders.terms("logoAgg").field("logo")));
        //分类的聚合
        sourceBuilder.aggregation(AggregationBuilders.terms("categoryIdAgg").field("categoryId")
                .subAggregation(AggregationBuilders.terms("categoryNameAgg").field("categoryName")));
        //规格参数的聚合
        sourceBuilder.aggregation(AggregationBuilders.nested("attrAgg","searchAttrs")
                .subAggregation(AggregationBuilders.terms("attrIdAgg").field("searchAttrs.attrId")
                        .subAggregation(AggregationBuilders.terms("attrNameAgg").field("searchAttrs.attrName"))
                        .subAggregation(AggregationBuilders.terms("attrValueAgg").field("searchAttrs.attrValue"))));


        //结果集过滤
        sourceBuilder.fetchSource(new String[]{"skuId","title","subTitle","defaultImage","price"},null);
        System.out.println(sourceBuilder);
        return sourceBuilder;
    }

}
